package com.zzb.framework.validator.util;

import com.baomidou.mybatisplus.annotations.TableField;
import com.baomidou.mybatisplus.annotations.TableName;
import com.zzb.framework.utils.DateUtil;
import com.zzb.framework.utils.ReflectionUtils;
import com.zzb.framework.utils.SpringContextHolder;
import com.zzb.framework.validator.annotation.*;
import com.zzb.framework.validator.ret.ValidatorRet;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Date;

/**
 * 验证表单工具类
 * Created by zhangzhenbin on 2016/10/12.
 */
public class ValidatorUtils {
    private static Logger logger = LoggerFactory.getLogger(ValidatorUtils.class);
    private static JdbcTemplate jdbcTemplate;

    static {
        jdbcTemplate =  SpringContextHolder.getBean("jdbcTemplate");
    }

    //表单验证
    public static <T> ValidatorRet validate(T obj, Class<T> clazz) {
        ValidatorRet validatorRet = new ValidatorRet();
//        Field[] fields = clazz.getDeclaredFields();
        Field[] fields = ReflectionUtils.getAllDeclaredFields(clazz);
        for (Field field : fields) {
            if ((field.getModifiers() & Modifier.TRANSIENT) != 0) continue;

            try {
                Object o = MethodUtils.invokeMethod(obj,"get" + field.getName().substring(0,1).toUpperCase()+field.getName().substring(1),new Object[]{});
//                Object o = FieldUtils.readDeclaredField(obj, field.getName(), true);
                {
                    //验证是否为空
                    ValidateNotNull validateNotNull = field.getAnnotation(ValidateNotNull.class);
                    if (validateNotNull != null) {
                        if (o == null) {
                            ValidatorRetUtils.addMessage(validatorRet, field.getName(), validateNotNull.msg());
                        }
                    }
                }
                {
                    //验证字符串
                    ValidateString validateString = field.getAnnotation(ValidateString.class);
                    if (validateString != null) {
                        if (!(o instanceof String)) {
                            logger.error("[" + field.getName() + "]表单验证失败！ValidateString 数据类型必须是String");
                        } else {
                            String s = (String) o;
                            //遍历验证列表里面的方法
                            ValidateString.ValidatorMethod[] methods = validateString.method();
                            if (ArrayUtils.isNotEmpty(methods)) {
                                for (ValidateString.ValidatorMethod method : methods) {
                                    String validateMethodCode = method.getCode();
                                    //如果是REQUIRED方法就验证是否是空
                                    if (validateMethodCode.equals(ValidateString.ValidatorMethod.REQUIRED.getCode())) {
                                        if (StringUtils.isBlank(s)) {
                                            ValidatorRetUtils.addMessage(validatorRet, field.getName(), method.getErrorMsg());
                                        }
                                    }
                                    //其他方法验证正则
                                    else {
                                        String pattern = method.getPattern();
                                        if (!simpleMatch(s, pattern)) {
                                            ValidatorRetUtils.addMessage(validatorRet, field.getName(), method.getErrorMsg());
                                        }
                                    }
                                }
                            }
                            //验证长度
                            if (validateString.length() != 0) {
                                if (StringUtils.length(s) > validateString.length()) {
                                    ValidatorRetUtils.addMessage(validatorRet, field.getName(), validateString.msg());
                                }
                            }
                            //验证自定义正则
                            if (StringUtils.isNotBlank(validateString.pattern())) {
                                if (!simpleMatch(s, validateString.pattern())) {
                                    ValidatorRetUtils.addMessage(validatorRet, field.getName(), validateString.msg());
                                }
                            }
                        }
                    }
                }
                {
                    //验证日期
                    ValidateDate validateDate = field.getAnnotation(ValidateDate.class);
                    if (validateDate != null) {
                        Date d = null;
                        //如果参数是字符串类型的，则转为date类型
                        if (o instanceof String) {
                            d = DateUtil.stringForceToDate((String) o);
                        } else if (o instanceof Date) {
                            d = (Date) o;
                        } else {
                            logger.error("[" + field.getName() + "]表单验证失败！ValidateDate 数据类型必须是Date或yyyy-MM-dd/yyyy-MM-dd HH:mm:ss/yyyyMMdd 类型的字符串");
                        }
                        //如果d为空，则表示验证失败
                        if (d == null) {
                            ValidatorRetUtils.addMessage(validatorRet, field.getName(), validateDate.msg());
                        } else {
                            if (StringUtils.isNotBlank(validateDate.after())) {
                                Date after = DateUtil.stringForceToDate(validateDate.after());
                                if (after == null) {
                                    logger.error("[" + field.getName() + "]表单验证失败！validateDate.after()验证规则必须是yyyy-MM-dd/yyyy-MM-dd HH:mm:ss/yyyyMMdd 类型的字符串");
                                } else {
                                    if (d.compareTo(after) < 0) {
                                        ValidatorRetUtils.addMessage(validatorRet, field.getName(), validateDate.msg());
                                    }
                                }
                            }
                            if (StringUtils.isNotBlank(validateDate.before())) {
                                Date before = DateUtil.stringForceToDate(validateDate.before());
                                if (before == null) {
                                    logger.error("[" + field.getName() + "]表单验证失败！validateDate.before()验证规则必须是yyyy-MM-dd/yyyy-MM-dd HH:mm:ss/yyyyMMdd 类型的字符串");
                                } else {
                                    if (d.compareTo(before) > 0) {
                                        ValidatorRetUtils.addMessage(validatorRet, field.getName(), validateDate.msg());
                                    }
                                }
                            }
                        }
                    }
                }
                {
                    //验证数字
                    ValidateNumber validateNumber = field.getAnnotation(ValidateNumber.class);
                    if (validateNumber != null) {
                        Double n = null;
                        if (o instanceof Number) {
                            try {
                                n = ((Number) o).doubleValue();
                            } catch (Exception e) {
                                logger.error("[" + field.getName() + "]表单验证失败！参数类型必须为数字或可转换成数字的字符串");
                            }
                        } else if (o instanceof String) {
                            try {
                                n = Double.parseDouble((String) o);
                            } catch (Exception e) {
                                logger.error("[" + field.getName() + "]表单验证失败！参数类型必须为数字或可转换成数字的字符串");
                            }
                        }
                        if (n != null) {
                            if (StringUtils.isNotBlank(validateNumber.min())) {
                                try {
                                    double min = Double.parseDouble(validateNumber.min());
                                    if (min > n) {
                                        ValidatorRetUtils.addMessage(validatorRet, field.getName(), validateNumber.msg());
                                    }
                                } catch (Exception e) {
                                    logger.error("[" + field.getName() + "]表单验证失败！validateNumber.min()验证规则必须可转换成数字的字符串");
                                }
                            }
                            if (StringUtils.isNotBlank(validateNumber.max())) {
                                try {
                                    double max = Double.parseDouble(validateNumber.max());
                                    if (max < n) {
                                        ValidatorRetUtils.addMessage(validatorRet, field.getName(), validateNumber.msg());
                                    }
                                } catch (Exception e) {
                                    logger.error("[" + field.getName() + "]表单验证失败！validateNumber.max()验证规则必须可转换成数字的字符串");
                                }
                            }
                        }
                    }
                }
                {
                    //验证关联
                    ValidateRef validateRef = field.getAnnotation(ValidateRef.class);
                    if (validateRef != null && jdbcTemplate != null) {
                        String tableName = null;
                        String refName = null;

                        try {
                            Class entity = validateRef.entity();
                            TableName annotation = (TableName) entity.getAnnotation(TableName.class);

                            if(annotation != null){
                                tableName = annotation.value();
                            } else {
                                tableName = entity.getSimpleName();
                            }
                        } catch (Exception e) {
                            logger.error("[" + field.getName() + "]表单验证失败！validateRef.entity()必填，且对应的class有@com.legendshop.dao.persistence.Table注解，且Table注解中name有值");
                        }
                        if (StringUtils.isNotBlank(tableName)) {
                            try {
                                String getMethodName = "get" + validateRef.ref().substring(0, 1).toUpperCase() + validateRef.ref().substring(1);
                                Method method = ReflectionUtils.getDeclaredMethod(clazz, getMethodName);
                                TableField columnAnnotation = method.getAnnotation(TableField.class);
                                if(columnAnnotation != null){
                                    refName = columnAnnotation.value();
                                } else {
                                    refName = validateRef.ref();
                                }
                            } catch (Exception e) {
                                logger.error("[" + field.getName() + "]表单验证失败！validateRef.ref()必填，且对应的属性的get方法上有@com.legendshop.dao.persistence.Column注解，且Column注解中name有值");
                            }
                        }

                        if (StringUtils.isNotBlank(tableName) && StringUtils.isNotBlank(refName)) {
                            try {
                                Long count = jdbcTemplate.
                                        queryForObject("select count(*) from " + tableName + " where " + refName + " = ?", Long.class, o);
                                if (count == null || count == 0) {
                                    ValidatorRetUtils.addMessage(validatorRet, field.getName(), validateRef.msg());
                                }
                            } catch (DataAccessException e) {
                                logger.error("[" + field.getName() + "]表单验证失败！关联查询失败");
                            }
                        }
                    }
                }

            } catch (Exception e) {
                logger.warn(e.getMessage());
            }
        }
        return validatorRet;
    }

    public static boolean simpleMatch(String str, String pattern) {
        return str.matches(pattern);
    }

    public static void main(String[] args) {
        System.out.println(simpleMatch("11g1111", "\\d{6}"));
    }
}
